#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/ftp.h>
#include <arpa/inet.h>

#include <functional>
#include <cerrno>
#include <iostream>
#include <cstring>
#include <fstream>
#include <unistd.h>
//C++11的线程库
#include <thread>

void Usage(const std::string& process)
{
    std::cout<<"Usage:"<<process<<" server_ip server_port\n";
}

int main(int argc,char* argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        return 1;
    }
    std::string server_ip = argv[1];
    uint16_t server_port = std::stoi(argv[2]);

    int sock = socket(AF_INET,SOCK_DGRAM,0);
    if(sock<0)
    {
        std::cerr<<"socket error:"<<strerror(errno)<<std::endl;
    }
    std::cout<<"create socket success , sock is:"<<sock<<"\n";
    //客户端需要 bind，但事实发送数据时bind，但是不显示bind，且一般在发送的时候隐式bind，不行甩开程序员来bind
    //server的端口不可改变，但是client时随机端口
    //为什么客户算需要随即呢？因为客户端十分多，如果占用就会出现问题

    //填充server信息
    struct sockaddr_in server;
    memset(&server,0,sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(server_port);
    server.sin_addr.s_addr = inet_addr(server_ip.c_str()); 

    //构建发送线程和接受线程
    std::function<void()> sendfunc = [sock,&server](){
            while(1)
            {
                std::string inbuffer; 
                std::cout<<"Please Enter\n";
                std::getline(std::cin,inbuffer);


                //发消息给server
                ssize_t n =  sendto(sock,inbuffer.c_str(),inbuffer.size(),0,(struct sockaddr*)&server,sizeof(server));

            }
    };


    std::function<void()> recvfunc = [sock](){
        while(1){
            char buffer[1024] = {0};

            //因为recv会接受发送方的一些属性，所以我们需要一个接受，但是CLient是已经有了
            //可以暂时不管
            //为了防止传输空指针出现没必要的错误
            //还是传入参数
            struct sockaddr_in  temp;
            socklen_t len = sizeof(temp);
            ssize_t re_n =  recvfrom(sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&temp,&len);

            //可以把读写的内容写进文件，在另外一个终端读出来，这样就能实现消息分割了。
            //拿如何解决命名问题呢.发送方与接受方的的ip等一些信息命名即可
            //当然这只是提供参考
            std::ofstream ofs("usr_1");
            
            if(re_n>0)      
            {
                // ofs<<buffer;
                // ofs.flush();

                //也可以狡猾一些，这里用cerr，给整个程序重定向cerr到一个文件，然后将其读取
                std::cout<<buffer<<"\n";
            }
            else
            {
                break;
            }

        }
    };
    std::thread Sender(sendfunc);
    std::thread recver(recvfunc);


    //初版CLinet收发消息
    //     while(1)
    //     {
    //         std::string inbuffer; 
    //         std::cout<<"Please Enter\n";
    //         std::getline(std::cin,inbuffer);


    //         //发消息给server
    //         ssize_t n =  sendto(sock,inbuffer.c_str(),inbuffer.size(),0,(struct sockaddr*)&server,sizeof(server));
    //     if(n>0)
    //     {
    //         //sent success
    //         char buffer[1024] = {0};

    //         //因为recv会接受发送方的一些属性，所以我们需要一个接受，但是CLient是已经有了
    //         //可以暂时不管
    //         //为了防止传输空指针出现没必要的错误
    //         //还是传入参数
    //         struct sockaddr_in  temp;
    //         socklen_t len = sizeof(temp);
    //         ssize_t re_n =  recvfrom(sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&temp,&len);

    //         if(re_n>0)
    //         {
    //             std::cout<<buffer<<"\n";
    //         }
    //         else
    //         {
    //             break;
    //         }
    //     }
    //     else
    //     {
    //         break;
    //     }

    // }
    Sender.join();
    recver.join();
    close(sock);

    return 0;
}
